热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

池子比率:BSV区块链上的去中心化金融应用——Uniswap分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。 Uniswap Uniswap 是所谓的 Decentrali

篇首语:本文由编程笔记#小编为大家整理,主要介绍了BSV 区块链上的 DeFi :Uniswap相关的知识,希望对你有一定的参考价值。



Uniswap

Uniswap 是所谓的 Decentralized Exchange,它允许个人或称为流动性提供者,将 Token 汇集到智能合约中提供流动性。


概述

我们实现了 Uniswap V1,它只在 BSV 和 Token 之间直接交换。如@state 装饰器所示,我们使用一个带有 @state 装饰器 的有状态合约来表示池子。它包含两个 Token :一个用于我们正在交换的 Token (第 7 行),另一个是治理 Token (第 11 行),称为流动性池(LP) Token 。该池将 BSV 直接存储在 UTXO 中(以 satoshis 为单位),将 Token 存储在对应的公钥哈希下(第 3 行)。

contract Uniswap
// pool's public key
PubKey poolPubkey;
// the main token
@state
ERC20 token;
// the liquidity pool governance token
@state
ERC20 lpToken;
...



Uniswap 合约源代码

增加流动性

任何人都可以通过调用函数 addLiquidity 向池中添加流动性。有两种情况(在第 9 行检查):


  1. 首次增加流动性:可以存入任意数量的 BSV 和 Token 。
  2. 添加更多流动性: BSV 和 Token 存入的比率必须与池中的现有比率相匹配(第 22 行)。

// add bsv and token to liquidity pool
public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, int lpSenderKeyIndex, int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// mint new lp tokens for the liquidity provider
if (oldBsvBalance == 0)
// initialize pool

// initially, just mint new lp tokens per the amount of new bsvs deposited
int lpMint = newBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));
else
// add more liquidity
int bsvAmount = newBsvBalance - oldBsvBalance;

// deposit ratio must be the same with current pool ration
// i.e., oldBsvBalance / oldTokenBalance == bsvAmount / tokenAmount
require(oldBsvBalance * tokenAmount == bsvAmount * oldTokenBalance);
// mint new lp tokens, proportinal to the amount of new bsvs deposited
int lpMint = this.lpToken.totalSupply() * bsvAmount / oldBsvBalance;
require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex));

// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

存入 BSV 后,新的 LP Token 在第 26 行按比例铸造给流动性提供者。 Token 在第 30 行转移到池子对应的账户。

例如,如果池中有 10 个 BSV 和 100 个 LP Token ,而 Alice 又向其中存入了 5 个 BSV ,则将向她铸造 50 个新的 LP Token 。


移除流动性

流动性提供者调用函数 removeLiquidity 来提取他们的资金,包括 BSV 和 Token 。

// remove bsv and token from liquidity pool
public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);

// withdraw amount
int bsvAmount = oldBsvBalance * lpAmount / this.lpToken.totalSupply();
int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();
// burn the lp tokens
require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
// transfer bsvs to the sender
int newBsvBalance = oldBsvBalance - bsvAmount;

require(this.propagateState(newBsvBalance, txPreimage));

流动性提供者拥有的 LP Token 的数量来提取流动性(第 9 行和第 10 行)。提款后,LP Token 在第 13 行被烧毁。第 16 行将 Token 从池子中转移到流动性提供者。第 18 行和第 20 行对 BSV 做同样的事情。

请注意,除了合约输出之外,还需要在同一交易中的另一个输出将 BSV 返回给流动性提供者。


BSV -> Token

用户调用函数 swapBsvToToken 将 BSV 兑换成 Token 。在池子收到 BSV 后(在第 7 行计算),第 10 行计算要返回的 Token 数量,第 13 行将它们返回给用户。

// swap bsvs for tokens
public function swapBsvToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance,
int newBsvBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
int bsvAmount = newBsvBalance - oldBsvBalance;
// calculate tokens in return
int tokensAmount = this.getAmount(bsvAmount, oldBsvBalance, oldTokenBalance);
// transfer tokens from pool to the sender
require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));
require(this.propagateState(newBsvBalance, txPreimage));


Token -> BSV

用户调用函数 swapTokenToBsv 将 Token 兑换成 BSV 。第 9 行计算要返回的 BSV 数量。第 13 行将 Token 转移到池子中。

// swap tokens for bsvs
public function swapTokenToBsv(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance,
int lpSenderBalance, SigHashPreimage txPreimage
)
require(checkSig(senderSig, sender));
int oldBsvBalance = SigHash.value(txPreimage);
// calculate bsvs in return
int bsvsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBsvBalance);
int newBsvBalance = oldBsvBalance - bsvsAmount;
// transfer tokens to the pool
require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex));

require(this.propagateState(newBsvBalance, txPreimage));

removeLiquidity 类似,需要另一个输出来将 BSV 返回给用户。


讨论

我们已经演示了如何在 BSV 区块链上实现一个基本的类似 Uniswap 的合约。有很多方法可以扩展它以使其更实用。


  • 价格公式:我们使用以下代码来确定价格,仅基于 BSV 和 Token 储备。它被称为恒定和公式,可能导致池子被排空。为了避免耗尽,可以使用更复杂的公式,如恒定乘积公式 (x * y = k),如 Uniswap 中。

// use reserve ratio as price
function getAmount(int input, int inputReserve, int outputReserve) : int
return outputReserve * input / inputReserve;


  • 流动性挖矿:我们可以对每次交换收取费用,并用这些费用来奖励流动性提供者。
  • 允许用户直接将一个 Token 换成另一个 Token 。

TokenSwap 实际上已经实现了以上所有以及更多。


致谢

本作品灵感来源于陈诚的这篇文章。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 深入解析JVM垃圾收集器
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版,详细探讨了JVM中不同类型的垃圾收集器及其工作原理。通过介绍各种垃圾收集器的特性和应用场景,帮助读者更好地理解和优化JVM内存管理。 ... [详细]
  • 本文介绍如何解决在 IIS 环境下 PHP 页面无法找到的问题。主要步骤包括配置 Internet 信息服务管理器中的 ISAPI 扩展和 Active Server Pages 设置,确保 PHP 脚本能够正常运行。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
author-avatar
爱旅游的星琴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有